﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Linq;

namespace Gmantis.Controls.DataGrid
{
    [TemplatePart(Name = "Cells", Type = typeof(DataGridCellsPanel)), 
    TemplatePart(Name = "HorizontalGridLine", Type = typeof(FrameworkElement)), 
    TemplateVisualState(Name = "Valid", GroupName = "ValidationStates"), 
    TemplateVisualState(Name = "InvalidFocused", GroupName = "ValidationStates"), 
    TemplateVisualState(Name = "InvalidUnfocused", GroupName = "ValidationStates")]
    public class DataGridRowPresenter : Control, IRecyclable, INotifyPropertyChanged
    {
        // Fields
        internal DataGridCellsPanel _elementCells;
        internal FrameworkElement _elementHorizontalGridLine;
        private ViewRow _viewRow;
        public static readonly DependencyProperty BackgroundBrushProperty = DependencyProperty.Register("BackgroundBrush", typeof(Brush), typeof(DataGridRowPresenter), null);
        public static readonly DependencyProperty CellsAreaMarginProperty = DependencyProperty.Register("CellsAreaMargin", typeof(Thickness), typeof(DataGridRowPresenter), null);
        internal const string CellsElementName = "Cells";
        public static readonly DependencyProperty HorizontalGridLineBrushProperty = DependencyProperty.Register("HorizontalGridLineBrush", typeof(Brush), typeof(DataGridRowPresenter), new PropertyMetadata(new SolidColorBrush()));
        internal const string HorizontalGridLineElementName = "HorizontalGridLine";
        public static readonly DependencyProperty HorizontalGridLineOpacityProperty = DependencyProperty.Register("HorizontalGridLineOpacity", typeof(double), typeof(DataGridRowPresenter), new PropertyMetadata(1.0));
        public static readonly DependencyProperty HorizontalGridLineVisibilityProperty = DependencyProperty.Register("HorizontalGridLineVisibility", typeof(Visibility), typeof(DataGridRowPresenter), new PropertyMetadata(Visibility.Collapsed));
        public static readonly DependencyProperty RowAreaMarginProperty = DependencyProperty.Register("RowAreaMargin", typeof(Thickness), typeof(DataGridRowPresenter), null);
        public static readonly DependencyProperty ValidationBackgroundProperty = DependencyProperty.Register("ValidationBackground", typeof(Brush), typeof(DataGridRowPresenter), null);
        public static readonly DependencyProperty ValidationResultsProperty = DependencyProperty.Register("ValidationResults", typeof(ICollection<ValidationResult>), typeof(DataGridRowPresenter), null);
        public static readonly DependencyProperty ValidationStateProperty = DependencyProperty.Register("ValidationState", typeof(ValidationState), typeof(DataGridRowPresenter), new PropertyMetadata(new PropertyChangedCallback(DataGridRowPresenter.OnValidationStatePropertyChanged)));
        public static readonly DependencyProperty VerticalGridLineBrushProperty = DependencyProperty.Register("VerticalGridLineBrush", typeof(Brush), typeof(DataGridRowPresenter), null);
        public static readonly DependencyProperty VerticalGridLineVisibilityProperty = DependencyProperty.Register("VerticalGridLineVisibility", typeof(Visibility), typeof(DataGridRowPresenter), new PropertyMetadata(Visibility.Visible));

        // Events
        public event MouseButtonEventHandler MouseLeftButtonDown;

        public event PropertyChangedEventHandler PropertyChanged;

        // Methods
        public DataGridRowPresenter()
        {
            RoutedEventHandler handler = null;
            base.DefaultStyleKey = typeof(DataGridRowPresenter);
            if (handler == null)
            {
                handler = (param0, param1) => this.ChangeVisualStateValidation(false);
            }
            base.Loaded += handler;
            this.SetCustomDefaultValues();
        }

        void IRecyclable.Collect()
        {
            this.IsCollected = true;
            this.ViewRow.FinalizeRowPresenter();
        }

        object IRecyclable.GetRecyclingKey()
        {
            if (this.ViewRow == null)
            {
                return null;
            }
            return this.ViewRow.GetRowPresenterRecyclingKey();
        }

        void IRecyclable.Reuse()
        {
            this.IsCollected = false;
            this.InvalidateCellsMeasure();
        }

        protected void ChangeVisualStateValidation(bool useTransitions)
        {
            if (this.ValidationState == ValidationState.InvalidUnfocused)
            {
                VisualStateHelper.GoToState(this, "InvalidUnfocused", useTransitions);
            }
            if (this.ValidationState == ValidationState.InvalidFocused)
            {
                VisualStateHelper.GoToState(this, "InvalidFocused", useTransitions);
            }
            if (this.ValidationState == ValidationState.Valid)
            {
                VisualStateHelper.GoToState(this, "Valid", useTransitions);
            }
        }

        internal virtual void EditCompleted(ViewColumn viewColumn)
        {
            if (this.CellsPanel != null)
            {
                this.CellsPanel.EditCompleted(viewColumn);
            }
        }

        private T GetTemplateChild<T>(string childName, bool required, ref string errors) where T : class
        {
            DependencyObject templateChild = base.GetTemplateChild(childName);
            ApplyTemplateHelper.VerifyTemplateChild(typeof(T), "template part", childName, templateChild, required, ref errors);
            return (templateChild as T);
        }

        private static Storyboard GetTemplateChildResource(FrameworkElement root, string resourceName, bool required, ref string errors)
        {
            return GetTemplateChildResource<Storyboard>(root, resourceName, required, ref errors);
        }

        private static T GetTemplateChildResource<T>(FrameworkElement root, string resourceName, bool required, ref string errors) where T : class
        {
            object child = root.Resources[resourceName];
            ApplyTemplateHelper.VerifyTemplateChild(typeof(T), "resource", resourceName, child, required, ref errors);
            return (child as T);
        }

        private void InitializeCellsPart()
        {
            this.CellsPanel = this._elementCells;
            if (this.CellsPanel != null)
            {
                this.CellsPanel.RowPresenter = this;
            }
        }

        private void InitializeDetailsPart()
        {
        }

        internal void InvalidateCellsArrange()
        {
            if (this.CellsPanel != null)
            {
                this.CellsPanel.InvalidateArrange();
            }
        }

        internal void InvalidateCellsMeasure()
        {
            if (this.CellsPanel != null)
            {
                this.CellsPanel.InvalidateMeasure();
            }
        }

        protected internal virtual void Load()
        {
            this.UpdateSelection();
            this.UpdateValidationState();
        }

        private void OnAfterApplyTemplate()
        {
            this.UpdateLines();
        }

        public override void OnApplyTemplate()
        {
            this.OnBeforeApplyTemplate();
            string errors = string.Empty;
            base.OnApplyTemplate();
            this._elementCells = this.GetTemplateChild<DataGridCellsPanel>("Cells", false, ref errors);
            if (this._elementCells != null)
            {
                this.InitializeCellsPart();
            }
            this._elementHorizontalGridLine = this.GetTemplateChild<FrameworkElement>("HorizontalGridLine", false, ref errors);
            if (!string.IsNullOrEmpty(errors))
            {
                if ((base.Template != null) && !PlatformIndependent.IsInDesignMode(this))
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Template cannot be applied to DataGridRowPresenter.\nDetails: {0}", new object[] { errors }));
                }
            }
            else
            {
                this.ChangeVisualStateValidation(false);
                this.OnAfterApplyTemplate();
            }
        }

        private void OnBeforeApplyTemplate()
        {
            if (this.CellsPanel != null)
            {
                this.CellsPanel.RemoveChildren();
            }
        }

        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            if (this.MouseLeftButtonDown != null)
            {
                this.MouseLeftButtonDown(this, e);
            }
            if (!e.Handled)
            {
                base.OnMouseLeftButtonDown(e);
            }
        }

        private static void OnValidationStatePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as DataGridRowPresenter).ChangeVisualStateValidation(true);
        }

        protected void RaisePropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private void SetCustomDefaultValues()
        {
            base.IsTabStop = false;
        }

        internal virtual FrameworkElement SetEditingCell(ViewColumn viewColumn)
        {
            if (this.CellsPanel != null)
            {
                return this.CellsPanel.SetEditingCell(viewColumn);
            }
            return null;
        }

        protected internal virtual void Unload()
        {
            if (this.CellsPanel != null)
            {
                this.CellsPanel.RemoveChildren();
            }
        }

        public virtual void UpdateCurrentCell()
        {
            if (this.CellsPanel != null)
            {
                this.CellsPanel.UpdateCurrentCell();
            }
        }

        public virtual void UpdateEditingRow()
        {
        }

        public virtual void UpdateLines()
        {
            if (this.CellsPanel != null)
            {
                this.CellsPanel.UpdateLines();
            }
            this.VerticalGridLineVisibility = ((this.DataGrid.GridLinesVisibility & DataGridGridLinesVisibility.Vertical) == DataGridGridLinesVisibility.Vertical) ? Visibility.Visible : Visibility.Collapsed;
            this.VerticalGridLineBrush = this.DataGrid.VerticalGridLinesBrush;
        }

        public virtual void UpdateMouseOver()
        {
            if (this.CellsPanel != null)
            {
                this.CellsPanel.UpdateMouseOver();
            }
        }

        public virtual void UpdateSelection()
        {
            if (this.CellsPanel != null)
            {
                this.CellsPanel.UpdateSelection();
            }
        }

        public virtual void UpdateValidationState()
        {
            this.ValidationResults = (from e in this.Row.Errors select new ValidationResult(e.Message, e.ColumnNames)).ToArray<ValidationResult>();
            this.ValidationState = ((this.Row.Errors.Count > 0) && this.View.DataGrid.ShowValidationIndicators) ? ValidationState.InvalidUnfocused : ValidationState.Valid;
            if (this.CellsPanel != null)
            {
                this.CellsPanel.UpdateValidationState();
            }
        }

        // Properties
        [Obsolete("Use Background property instead.", true)]
        public Brush BackgroundBrush
        {
            get
            {
                return (Brush)base.GetValue(BackgroundBrushProperty);
            }
            set
            {
                base.SetValue(BackgroundBrushProperty, value);
            }
        }

        bool IRecyclable.IsCollected
        {
            get
            {
                return this.IsCollected;
            }
        }

        public Thickness CellsAreaMargin
        {
            get
            {
                return (Thickness)base.GetValue(CellsAreaMarginProperty);
            }
            set
            {
                base.SetValue(CellsAreaMarginProperty, value);
            }
        }

        public DataGridCellsPanel CellsPanel { get; private set; }

        public DataGrid DataGrid
        {
            get
            {
                if (this.ViewRow == null)
                {
                    return null;
                }
                return this.ViewRow.DataGrid;
            }
        }

        public Brush HorizontalGridLineBrush
        {
            get
            {
                return (Brush)base.GetValue(HorizontalGridLineBrushProperty);
            }
            set
            {
                base.SetValue(HorizontalGridLineBrushProperty, value);
            }
        }

        public double HorizontalGridLineOpacity
        {
            get
            {
                return (double)base.GetValue(HorizontalGridLineOpacityProperty);
            }
            set
            {
                base.SetValue(HorizontalGridLineOpacityProperty, value);
            }
        }

        public Visibility HorizontalGridLineVisibility
        {
            get
            {
                return (Visibility)base.GetValue(HorizontalGridLineVisibilityProperty);
            }
            set
            {
                base.SetValue(HorizontalGridLineVisibilityProperty, value);
            }
        }

        internal bool IsCollected { get; set; }

        internal bool IsLoading { get; set; }

        public DataGridCellPresenter this[DataGridColumn column]
        {
            get
            {
                if (this.ViewRow != null)
                {
                    return this.ViewRow.GetCellPresenter(column.ViewColumn, false);
                }
                return null;
            }
        }

        public DataGridRow Row
        {
            get
            {
                if (this.ViewRow == null)
                {
                    return null;
                }
                return this.ViewRow.Row;
            }
        }

        public Thickness RowAreaMargin
        {
            get
            {
                return (Thickness)base.GetValue(RowAreaMarginProperty);
            }
            set
            {
                base.SetValue(RowAreaMarginProperty, value);
            }
        }

        public Brush ValidationBackground
        {
            get
            {
                return (Brush)base.GetValue(ValidationBackgroundProperty);
            }
            set
            {
                base.SetValue(ValidationBackgroundProperty, value);
            }
        }

        public ICollection<ValidationResult> ValidationResults
        {
            get
            {
                return (ICollection<ValidationResult>)base.GetValue(ValidationResultsProperty);
            }
            set
            {
                base.SetValue(ValidationResultsProperty, value);
            }
        }

        public ValidationState ValidationState
        {
            get
            {
                return (ValidationState)base.GetValue(ValidationStateProperty);
            }
            set
            {
                base.SetValue(ValidationStateProperty, value);
            }
        }

        public Brush VerticalGridLineBrush
        {
            get
            {
                return (Brush)base.GetValue(VerticalGridLineBrushProperty);
            }
            set
            {
                base.SetValue(VerticalGridLineBrushProperty, value);
            }
        }

        public Visibility VerticalGridLineVisibility
        {
            get
            {
                return (Visibility)base.GetValue(VerticalGridLineVisibilityProperty);
            }
            set
            {
                base.SetValue(VerticalGridLineVisibilityProperty, value);
            }
        }

        internal DataGridView View
        {
            get
            {
                if (this.ViewRow == null)
                {
                    return null;
                }
                return this.ViewRow.View;
            }
        }

        internal ViewRow ViewRow
        {
            get
            {
                return this._viewRow;
            }
            set
            {
                if (this._viewRow != value)
                {
                    this._viewRow = value;
                    this.RaisePropertyChanged("Row");
                }
            }
        }
    }
}
